const { SyncHook, AsyncSeriesHook, SyncBailHook, AsyncParallelHook } = require('tapable')
const NormalModuleFactory = require('./NormalModuleFactory')
const Compilation = require('./Compilation')
const path = require(path)
const mkdirp = require('mkdirp')

class Compiler {
  constructor(context) {
    this.hooks = {
      done: new AsyncSeriesHook(['stats']),
      entryOption: new SyncBailHook(['context', 'entry']),

      beforeRun: new AsyncSeriesHook(["compiler"]),
      run: new AsyncSeriesHook(["compiler"]),

      beforeCompile: new AsyncSeriesHook(['params']),
      compile: new SyncHook(["params"]),
      thisCompilation: new SyncHook(["compilation", "params"]),
      compilation: new SyncHook(["compilation", "params"]),

      make: new AsyncParallelHook(['compilation']),
      afterCompile: new AsyncSeriesHook(['compilation']),

      emit: new AsyncSeriesHook(['compilation'])
    }
  }

  emitAsstes (compilation, callback) {
    // 创建输出目录，把文件写入创建出的目录
    const emitFiles = (err) => {
      const assets = compilation.assets
      let outputPath = this.options.output.path

      for (const file in assets) {
        let source = assets[file]
        let targetPath = path.posix.join(outputPath, file)
        this.outputFileSystem.writeFileSync(targetPath, source, 'utf8')
      }
      callback(err)
    }

    // 创建目录，启动文件写入
    this.hooks.emit.callAsync(compilation, err => {
      mkdirp.sync(this.options.output.path)
      emitFiles(err)
    })
  }

  run (callback) {
    const finalCallBack = (err, stats) => {
      callback(err, stats)
    }

    const onCompiled = (err, compilation) => {
      console.log('emit')
      // if (err) finalCallBack(err, {
      //   toJson () {
      //     return {
      //       entries: [],
      //       chunks: [],
      //       modules: [],
      //       assets: []
      //     }
      //   }
      // })
      this.emitAsstes(compilation, err => {
        let stats = new Stats(compilation)
        finalCallBack(err, stats)

      })
    }

    const run = () => {
      this.hooks.beforeRun.callAsync(this, err => {
        this.hooks.run.callAsync(this, err => {
          this.compile(onCompiled)
        })
      })
    }

    run()

  }

  compile (callback) {
    const params = this.newCompilationParams()
    this.hooks.beforeCompile.callAsync(params, err => {
      this.hooks.compile.call(params)

      const compilation = this.newCompilation(params)

      this.hooks.make.callAsync(compilation, err => {
        // make处理结束后，开始处理chunk
        compilation.seal((err) => {
          this.hooks.afterCompile.callAsync(compilation, err => {
            callback(err, compilation)
          })
        })
      })
    })
  }

  newCompilationParams () {
    const params = {
      normalMoudleFactory: new NormalModuleFactory()
    }

    return params
  }

  newCompilation (params) {
    const compilation = this.createCompilation()
    this.hooks.compilation.call(compilation, params)
    return compilation
  }

  createCompilation () {
    return new Compilation(this)
  }
}

module.exports = Compiler
